package com.cyou.weplay.redis;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.support.ResourceHolder;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;

/**
 * JedisConnectionUtils
 *
 * @author zhaomingyu
 */
public class JedisConnectionUtils {

    private static final Logger logger = LoggerFactory.getLogger(JedisConnectionUtils.class);

    public static JedisConnection getJedisConnection(ShardedJedisPool pool) {
        return getJedisConnection(pool, true);
    }

    public static JedisConnection getJedisConnection(ShardedJedisPool pool, boolean enableTranactionSupport) {
        JedisConnectionHolder jedisHolder = (JedisConnectionHolder) TransactionSynchronizationManager.getResource(pool);
        if (jedisHolder != null) {
            return jedisHolder.getJedisConnection();
        }

        ShardedJedis jedis = pool.getResource();
        JedisConnection conn = new JedisConnection(jedis);
        jedisHolder = new JedisConnectionHolder(conn);
        if (enableTranactionSupport) {
            potentiallyRegisterTransactionSynchronisation(jedisHolder, pool);
        }

        return conn;
    }

    public static void releaseJedisConnection(JedisConnection conn, ShardedJedisPool pool) {
        if (conn == null) {
            return;
        }
        JedisConnectionHolder jedisHolder = (JedisConnectionHolder) TransactionSynchronizationManager.getResource(pool);
        if (jedisHolder != null && jedisHolder.isTransactionSyncronisationActive()) {
            return;
        }
        if (!isTransactional(conn, pool)) {
            logger.debug("close jedis by releaseShardedJedis");
            conn.close();
        }
    }

    private static void potentiallyRegisterTransactionSynchronisation(JedisConnectionHolder jedisHolder, ShardedJedisPool pool) {
        if (isActualNonReadonlyTransactionActive()) {
            if (!jedisHolder.isTransactionSyncronisationActive()) {
                jedisHolder.setTransactionSyncronisationActive(true);
                TransactionSynchronizationManager.bindResource(pool, jedisHolder);
                TransactionSynchronizationManager.registerSynchronization(new ShardedJedisSynchronizer(jedisHolder));
            }
        }
    }

    private static boolean isActualNonReadonlyTransactionActive() {
        return TransactionSynchronizationManager.isActualTransactionActive()
                && !TransactionSynchronizationManager.isCurrentTransactionReadOnly();
    }

    private static boolean isTransactional(JedisConnection conn, ShardedJedisPool pool) {
        if (pool == null) {
            return false;
        }
        JedisConnectionHolder jedisHolder = (JedisConnectionHolder) TransactionSynchronizationManager.getResource(pool);
        return (jedisHolder != null) && conn == jedisHolder.getJedisConnection();
    }

    private static class JedisConnectionHolder implements ResourceHolder {

        private boolean unbound;

        private final JedisConnection conn;

        private boolean transactionSyncronisationActive;

        public JedisConnectionHolder(JedisConnection conn) {
            this.conn = conn;
        }

        @Override
        public void reset() {
        }

        @Override
        public void unbound() {
            this.unbound = true;
        }

        @Override
        public boolean isVoid() {
            return unbound;
        }

        public JedisConnection getJedisConnection() {
            return conn;
        }

        public boolean isTransactionSyncronisationActive() {
            return transactionSyncronisationActive;
        }

        public void setTransactionSyncronisationActive(boolean transactionSyncronisationActive) {
            this.transactionSyncronisationActive = transactionSyncronisationActive;
        }
    }

    private static class ShardedJedisSynchronizer extends TransactionSynchronizationAdapter {

        JedisConnectionHolder jedisHolder;

        public ShardedJedisSynchronizer(JedisConnectionHolder jedisHolder) {
            this.jedisHolder = jedisHolder;
        }

        @Override
        public void afterCompletion(int status) {
            logger.debug("close jedis by Synchronizer");
            jedisHolder.getJedisConnection().close();
        }
    }
}
